home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / papyon / p2p.py < prev    next >
Text File  |  2009-10-08  |  11KB  |  321 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # papyon - a python client library for Msn
  4. #
  5. # Copyright (C) 2007 Ali Sabil <ali.sabil@gmail.com>
  6. # Copyright (C) 2007 Johann Prieur <johann.prieur@gmail.com>
  7. # Copyright (C) 2008 Richard Spiers <richard.spiers@gmail.com>
  8. #
  9. # This program is free software; you can redistribute it and/or modify
  10. # it under the terms of the GNU General Public License as published by
  11. # the Free Software Foundation; either version 2 of the License, or
  12. # (at your option) any later version.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License
  20. # along with this program; if not, write to the Free Software
  21. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  22.  
  23. """P2P
  24. This module contains the classes needed to engage in a peer to peer transfer
  25. with a contact.
  26.     @group MSNObject: MSNObjectStore, MSNObject, MSNObjectType
  27.     @sort: MSNObjectStore, MSNObject, MSNObjectType"""
  28. from msnp2p.msnobject import MSNObjectSession
  29. from msnp2p.webcam import WebcamSession
  30. from msnp2p import EufGuid, ApplicationID
  31. from msnp2p.exceptions import ParseError
  32. from profile import NetworkID, Contact, Profile
  33.  
  34. import papyon.util.element_tree as ElementTree
  35. import papyon.util.string_io as StringIO
  36.  
  37. import gobject
  38. import xml.sax.saxutils as xml
  39. import urllib
  40. import base64
  41. import hashlib
  42. import logging
  43.  
  44. __all__ = ['MSNObjectType', 'MSNObject', 'MSNObjectStore', 'WebcamHandler']
  45.  
  46. logger = logging.getLogger('papyon.p2p')
  47.  
  48. class MSNObjectType(object):
  49.     """Represent the various MSNObject types"""
  50.  
  51.     CUSTOM_EMOTICON = 2
  52.     "Custom smiley"
  53.     DISPLAY_PICTURE = 3
  54.     "Display picture"
  55.     BACKGROUND_PICTURE = 5
  56.     "Background picture"
  57.     DYNAMIC_DISPLAY_PICTURE = 7
  58.     "Dynamic display picture"
  59.     WINK = 8
  60.     "Wink"
  61.     VOICE_CLIP = 11
  62.     "Void clip"
  63.     SAVED_STATE_PROPERTY = 12
  64.     "Saved state property"
  65.     LOCATION = 14
  66.     "Location"
  67.  
  68. class MSNObject(object):
  69.     "Represents an MSNObject."
  70.     def __init__(self, creator, size, typ, location, friendly,
  71.                  shad=None, shac=None, data=None):
  72.         """Initializer
  73.  
  74.             @param creator: the creator of this MSNObject
  75.             @type creator: utf-8 encoded string representing the account
  76.  
  77.             @param size: the total size of the data represented by this MSNObject
  78.             @type size: int
  79.  
  80.             @param typ: the type of the data
  81.             @type typ: L{MSNObjectType}
  82.  
  83.             @param location: a filename for the MSNObject
  84.             @type location: utf-8 encoded string
  85.  
  86.             @param friendly: a friendly name for the MSNObject
  87.             @type friendly: utf-8 encoded string
  88.  
  89.             @param shad: sha1 digest of the data
  90.  
  91.             @param shac: sha1 digest of the MSNObject itself
  92.  
  93.             @param data: file object to the data represented by this MSNObject
  94.             @type data: File
  95.         """
  96.         # Backward compatible with older clients that pass a Contact/Profile
  97.         if type(creator) is Contact or type(creator) is Profile:
  98.             creator = creator.account
  99.         self._creator = creator
  100.         self._size = size
  101.         self._type = typ
  102.         self._location = location
  103.         self._friendly = friendly
  104.  
  105.         if shad is None:
  106.             if data is None:
  107.                 raise NotImplementedError
  108.             shad = self.__compute_data_hash(data)
  109.         self._data_sha = shad
  110.         self.__data = data
  111.         if shac is None:
  112.             shac = self.__compute_checksum()
  113.         self._checksum_sha = shac
  114.         self._repr = None
  115.  
  116.     def __ne__(self, other):
  117.         return not (self == other)
  118.  
  119.     def __eq__(self, other):
  120.         if other == None:
  121.             return False
  122.         return other._type == self._type and \
  123.             other._data_sha == self._data_sha
  124.  
  125.     def __hash__(self):
  126.         return hash(str(self._type) + self._data_sha)
  127.  
  128.     def __set_data(self, data):
  129.         if self._data_sha != self.__compute_data_hash(data):
  130.             logger.warning("Received data doesn't match the MSNObject data hash.")
  131.             return
  132.  
  133.         old_pos = data.tell()
  134.         data.seek(0, 2)
  135.         self._size = data.tell()
  136.         data.seek(old_pos, 0)
  137.  
  138.         self.__data = data
  139.         self._checksum_sha = self.__compute_checksum()
  140.     def __get_data(self):
  141.         return self.__data
  142.     _data = property(__get_data, __set_data)
  143.  
  144.     @staticmethod
  145.     def parse(client, xml_data):
  146.         data = StringIO.StringIO(xml_data)
  147.         try:
  148.             element = ElementTree.parse(data).getroot().attrib
  149.         except:
  150.             raise ParseError('Invalid MSNObject')
  151.  
  152.         creator = element["Creator"]
  153.         size = int(element["Size"])
  154.         type = int(element["Type"])
  155.         location = xml.unescape(element["Location"])
  156.         friendly = base64.b64decode(xml.unescape(element["Friendly"]))
  157.         shad = element.get("SHA1D", None)
  158.         if shad is not None:
  159.             shad = base64.b64decode(shad)
  160.         shac = element.get("SHA1C", None)
  161.         if shac is not None:
  162.             shac = base64.b64decode(shac)
  163.  
  164.         result = MSNObject(creator, size, type, location, friendly, shad, shac)
  165.         result._repr = xml_data
  166.         return result
  167.  
  168.     def __compute_data_hash(self, data):
  169.         digest = hashlib.sha1()
  170.         data.seek(0, 0)
  171.         read_data = data.read(1024)
  172.         while len(read_data) > 0:
  173.             digest.update(read_data)
  174.             read_data = data.read(1024)
  175.         data.seek(0, 0)
  176.         return digest.digest()
  177.  
  178.     def __compute_checksum(self):
  179.         input = "Creator%sSize%sType%sLocation%sFriendly%sSHA1D%s" % \
  180.             (self._creator, str(self._size), str(self._type),\
  181.                  str(self._location), base64.b64encode(self._friendly), \
  182.                  base64.b64encode(self._data_sha))
  183.         return hashlib.sha1(input).hexdigest()
  184.  
  185.     def __str__(self):
  186.         return self.__repr__()
  187.  
  188.     def __repr__(self):
  189.         if self._repr is not None:
  190.             return self._repr
  191.         dump = "<msnobj Creator=%s Type=%s SHA1D=%s Size=%s Location=%s Friendly=%s/>" % \
  192.             (xml.quoteattr(self._creator),
  193.                 xml.quoteattr(str(self._type)),
  194.                 xml.quoteattr(base64.b64encode(self._data_sha)),
  195.                 xml.quoteattr(str(self._size)),
  196.                 xml.quoteattr(str(self._location)),
  197.                 xml.quoteattr(base64.b64encode(self._friendly)))
  198.         return dump
  199.  
  200.  
  201. class MSNObjectStore(object):
  202.  
  203.     def __init__(self, client):
  204.         self._client = client
  205.         self._outgoing_sessions = {} # session => (handle_id, callback, errback)
  206.         self._incoming_sessions = {}
  207.         self._published_objects = set()
  208.  
  209.     def _can_handle_message (self, message):
  210.         euf_guid = message.body.euf_guid
  211.         if euf_guid == EufGuid.MSN_OBJECT:
  212.             return True
  213.         else:
  214.             return False
  215.  
  216.     def _handle_message(self, peer, message):
  217.         session = MSNObjectSession(self._client._p2p_session_manager,
  218.                 peer, message.body.application_id, message)
  219.  
  220.         handle_id = session.connect("completed",
  221.                         self._incoming_session_transfer_completed)
  222.         self._incoming_sessions[session] = handle_id
  223.         try:
  224.             msn_object = MSNObject.parse(self._client, session._context)
  225.         except ParseError:
  226.             session.reject()
  227.             return
  228.         for obj in self._published_objects:
  229.             if obj._data_sha == msn_object._data_sha:
  230.                 session.accept(obj._data)
  231.                 return session
  232.         session.reject()
  233.  
  234.     def request(self, msn_object, callback, errback=None, peer=None):
  235.         if msn_object._data is not None:
  236.             callback[0](msn_object, *callback[1:])
  237.  
  238.         if peer is None:
  239.             peer = self._client.address_book.search_contact(msn_object._creator,
  240.                     NetworkID.MSN)
  241.  
  242.         if msn_object._type == MSNObjectType.CUSTOM_EMOTICON:
  243.             application_id = ApplicationID.CUSTOM_EMOTICON_TRANSFER
  244.         elif msn_object._type == MSNObjectType.DISPLAY_PICTURE:
  245.             application_id = ApplicationID.DISPLAY_PICTURE_TRANSFER
  246.         else:
  247.             raise NotImplementedError
  248.  
  249.         session = MSNObjectSession(self._client._p2p_session_manager,
  250.                 peer, application_id)
  251.         handle_id = session.connect("completed",
  252.                 self._outgoing_session_transfer_completed)
  253.         self._outgoing_sessions[session] = \
  254.                 (handle_id, callback, errback, msn_object)
  255.         session.invite(repr(msn_object))
  256.  
  257.     def publish(self, msn_object):
  258.         if msn_object._data is None:
  259.             logger.warning("Trying to publish an empty MSNObject")
  260.         else:
  261.             self._published_objects.add(msn_object)
  262.  
  263.     def _outgoing_session_transfer_completed(self, session, data):
  264.         handle_id, callback, errback, msn_object = self._outgoing_sessions[session]
  265.         session.disconnect(handle_id)
  266.         msn_object._data = data
  267.  
  268.         callback[0](msn_object, *callback[1:])
  269.         del self._outgoing_sessions[session]
  270.  
  271.     def _incoming_session_transfer_completed(self, session, data):
  272.         handle_id = self._incoming_sessions[session]
  273.         session.disconnect(handle_id)
  274.         del self._incoming_sessions[session]
  275.  
  276. class WebcamHandler(gobject.GObject):
  277.  
  278.     __gsignals__ = {
  279.             "session-created" : (gobject.SIGNAL_RUN_FIRST,
  280.                 gobject.TYPE_NONE,
  281.                 (object, bool))
  282.     }
  283.  
  284.     def __init__(self, client):
  285.         gobject.GObject.__init__(self)
  286.         self._client = client
  287.         self._sessions = []
  288.  
  289.     def _can_handle_message (self, message):
  290.         euf_guid = message.body.euf_guid
  291.         if (euf_guid == EufGuid.MEDIA_SESSION or
  292.             euf_guid == EufGuid.MEDIA_RECEIVE_ONLY):
  293.             return True
  294.         else:
  295.             return False
  296.  
  297.     def _handle_message (self, peer, message):
  298.         euf_guid = message.body.euf_guid
  299.         if (euf_guid == EufGuid.MEDIA_SESSION):
  300.             producer = False
  301.         elif (euf_guid == EufGuid.MEDIA_RECEIVE_ONLY):
  302.             producer = True
  303.  
  304.         session = WebcamSession(producer, self._client._p2p_session_manager, \
  305.                                     peer, message.body.euf_guid, message)
  306.         self._sessions.append(session)
  307.         self.emit("session-created", session, producer)
  308.         return session
  309.  
  310.     def invite(self, peer, producer=True):
  311.         print "Creating New Send Session"
  312.         if producer:
  313.             euf_guid = EufGuid.MEDIA_SESSION
  314.         else:
  315.             euf_guid = EufGuid.MEDIA_RECEIVE_ONLY
  316.         session = WebcamSession(producer, self._client._p2p_session_manager, \
  317.                                     peer, euf_guid)
  318.         self._sessions.append(session)
  319.         session.invite()
  320.         return session
  321.